//
// Created by lvyer on 2021/6/7.
//
#include <compressorjpeg.h>


compressorjpeg::compressorjpeg(jobject bitmap, const char *outputPath, int quality,
                               jnicallbackhelper *helper) {
    //不能直接赋值，外部被释放之后会出现悬空指针，也就是这个也同样会没有内容了，所以需要使用深拷贝来完成赋值
    //Java层是 xxxxx/xxx.jpeg
    //C层是   xxxxx/xxx.jpeg\0  C层会自动加入\0
    //而strlen是不会计算最后一个 \0的数量的,所以我们新建数组的时候需要自己加入\0
    this->outputPath = new char[strlen(outputPath) + 1]; //C层字符串会自动加入 \0
    strcpy(this->outputPath, outputPath);
    this->helper = helper;
    this->rawBitmap = helper->env->NewGlobalRef(bitmap);
    this->quality = quality;
}

compressorjpeg::~compressorjpeg() {
    //释放所有堆区成员
    if (outputPath) {
        delete outputPath;
    }
    if (rawBitmap) {
        helper->env->DeleteGlobalRef(rawBitmap);
        rawBitmap = nullptr;
    }
    if (helper) {
        delete helper;
    }
}

void compressorjpeg::writeJpegToFile(uint8_t *temp, int width, int height, jint quality,
                                     const char *outPutPath) {
    if (DEBUG) LOG_DEBUG("开始写入磁盘阶段 path: %s", outPutPath);
    //创建jpeg压缩对象
    jpeg_compress_struct jcs;
    //错误回调
    jpeg_error_mgr error;
    jcs.err = jpeg_std_error(&error);
    //创建压缩对象
    jpeg_create_compress(&jcs);
    //指定存储文件  write binary 写入二进制模式
    FILE *f = fopen(outPutPath, "wb");
    jpeg_stdio_dest(&jcs, f);
    //设置压缩参数
    jcs.image_width = width;
    jcs.image_height = height;
    //bgr
    jcs.input_components = 3;
    jcs.in_color_space = JCS_RGB;
    jpeg_set_defaults(&jcs);
    //开启哈夫曼功能
    jcs.optimize_coding = true;
    jpeg_set_quality(&jcs, quality, 1);
    //开始压缩
    jpeg_start_compress(&jcs, 1);
    //循环写入每一行数据
    int row_stride = width * 3;//一行的字节数
    JSAMPROW row[1];
    while (jcs.next_scanline < jcs.image_height) {
        //取一行数据
        uint8_t *pixels = temp + jcs.next_scanline * row_stride;
        row[0] = pixels;
        jpeg_write_scanlines(&jcs, row, 1);
    }
    //压缩完成
    jpeg_finish_compress(&jcs);
    //释放jpeg对象
    fclose(f);
    jpeg_destroy_compress(&jcs);
    if (DEBUG) LOG_DEBUG("写入磁盘完成");
}

void compressorjpeg::handlerData(jobject bitmap, jint quality, const char *outPutPath) {


}

void compressorjpeg::runnable_compressor(int thread_mode) {
    if (DEBUG) LOG_DEBUG("压缩开始");
    JNIEnv *currentEnv;
    if (thread_mode == THREAD_MAIN) {
        currentEnv = helper->env;
    } else {
        //子线程原有env不可用
        helper->jvm->AttachCurrentThread(&currentEnv, 0);
    }
    //从bitmap中获取argb数据
    AndroidBitmapInfo info;
    //获取里面的信息
    AndroidBitmap_getInfo(currentEnv, rawBitmap, &info);
    // 得到图片中的像素信息
    uint8_t *pixels;// 相当于byte数组  *pixels === byte[]
    AndroidBitmap_lockPixels(currentEnv, rawBitmap, (void **) &pixels);
    //jpge argb去掉他的a ===> rgb
    //开一块内存用来存储rgb信息
    uint8_t *data = (uint8_t *) malloc(
            info.width * info.height * 3);// 可以用来存放图片所有信息(w*h为所有像素,每个像素有r,g,b三种颜色)
    uint8_t *temp = data;
    uint8_t r, g, b;
    int color;
    for (int i = 0; i < info.height; ++i) {
        for (int j = 0; j < info.width; ++j) {
            color = *(int *) pixels; // color 4个字节,argb
            r = (color >> 16) & 0xFF;
            g = (color >> 8) & 0xFF;
            b = color & 0xFF;
            // 存放颜色,jpeg的格式为bgr,倒过来存
            *data = b;
            *(data + 1) = g;
            *(data + 2) = r;
            data += 3;// 这里就去除了alpha通道
            pixels += 4;// 每个像素有argb4个通道,其中alpha通道被我们丢弃掉了,没有存到data里面
        }
    }
    //把得到的新的图片的信息存入一个新文件 中
    writeJpegToFile(temp, info.width, info.height, quality, outputPath);

    free(temp);// data是通过malloc开辟的,这里需要释放掉 temp
    AndroidBitmap_unlockPixels(currentEnv, rawBitmap);

    if (DEBUG) LOG_DEBUG("压缩完成");

    if (thread_mode == THREAD_MAIN) {
        currentEnv = nullptr;
    } else {
        //释放子线程创建的env
        helper->jvm->DetachCurrentThread();
        currentEnv = nullptr;
    }
    if (helper) {
        if (DEBUG) LOG_DEBUG("开始回调");
        helper->onResult(thread_mode, rawBitmap, outputPath);
    }
}

/**
 * 当选择openChild线程完成任务的时候，执行该线程方法来新建一个子线程进行后续操作
 * 该方法中主要是对线程进行管理，实际任务交由runnable_compressor完成
 * @param args
 * @return
 */
void *task_compressor(void *args) {
    compressorjpeg *player = static_cast<compressorjpeg *>(args);
    if (DEBUG) LOG_DEBUG("task_compressor 线程开始执行任务");
    player->runnable_compressor(THREAD_CHILD);
    return 0;
}

void compressorjpeg::compressor(bool openChild) {
    if (openChild) {
        //创建子线程
        pthread_create(&pid, NULL, task_compressor, this);
    } else {
        //直接在当前线程中执行
        runnable_compressor(THREAD_MAIN);
    }
}


